FlutterでFCMを使ったプッシュ通知を実装してみた
大阪オフィスの山田です。新しい冷蔵庫は自動で氷を作ってくれるのですごい。今回はFCMを使ってFlutterで作ったアプリでプッシュ通知を受け取れるように実装してみたのでご紹介します。
今回やること
FCM (Firebase Cloud Messaging) でプッシュ通知の環境を作って、Flutterで作ったアプリで通知を受け取ってみます。iOSで必要になるAPNs認証キーの作り方やFirebaseでプロジェクトを作る部分については説明しません。FlutterでFCMを利用するために、ライブラリ、firebase_messagingを使用します。
開発環境
flutter doctor
Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel beta, v0.5.1, on Mac OS X 10.13.5 17F77, locale ja-JP) [✓] Android toolchain - develop for Android devices (Android SDK 28.0.1) [✓] iOS toolchain - develop for iOS devices (Xcode 9.4) [✓] Android Studio (version 3.1) ✗ Flutter plugin not installed; this adds Flutter specific functionality. ✗ Dart plugin not installed; this adds Dart specific functionality. [!] VS Code (version 1.26.1) [✓] Connected devices (1 available)
flutter --version
Flutter 0.5.1 • channel beta • https://github.com/flutter/flutter.git Framework • revision c7ea3ca377 (3 months ago) • 2018-05-29 21:07:33 +0200 Engine • revision 1ed25ca7b7 Tools • Dart 2.0.0-dev.58.0.flutter-f981f09760
準備
iOS
- FirebaseプロジェクトにiOSアプリを追加して、
GoogleService-info.plist
をダウンロードします。 GoogleService-info.plist
をプロジェクトに追加します。XcodeでRunner.xcodeprojを開いて、Runnerディレクトリの下に追加します。
- プロジェクトの設定としてプッシュ通知を有効化します。Capabilitiesタブの
Push Notifications
をONにします。
ios/Podfile
のuse_frameworks!
の記述を削除します。- FirebaseプロジェクトにAPNs証明書、あるいはAPNs認証キーを設定します。
Android
- FirebaseプロジェクトにAndroidアプリを追加して、
google-services.json
をダウンロードします。 google-services.json
を、android/app
ディレクトリの直下に配置します。
以下のgradleファイルに記述を追加します。 - プロジェクトルートのbuild.gradle
dependencies { classpath 'com.google.gms:google-services:4.0.0' }
- アプリのbuild.gradle
dependencies { compile 'com.google.firebase:firebase-core:16.0.0' } apply plugin: 'com.google.gms.google-services' <- ファイルの最後に追記
- AndroidManifest.xml
<intent-filter> <action android:name="FLUTTER_NOTIFICATION_CLICK" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter>
Flutterの実装
pubspec.yamlに以下の記述を追加します。
dependencies: firebase_messaging: ^1.0.4
プログラムは以下のように書きました。
import 'package:firebase_messaging/firebase_messaging.dart'; // ライブラリのインポート // 以下をStateの中に記述 final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging(); @override void initState() { super.initState(); _firebaseMessaging.configure( onMessage: (Map<String, dynamic> message) async { print("onMessage: $message"); _buildDialog(context, "onMessage"); }, onLaunch: (Map<String, dynamic> message) async { print("onLaunch: $message"); _buildDialog(context, "onLaunch"); }, onResume: (Map<String, dynamic> message) async { print("onResume: $message"); _buildDialog(context, "onResume"); }, ); _firebaseMessaging.requestNotificationPermissions( const IosNotificationSettings(sound: true, badge: true, alert: true)); _firebaseMessaging.onIosSettingsRegistered .listen((IosNotificationSettings settings) { print("Settings registered: $settings"); }); _firebaseMessaging.getToken().then((String token) { assert(token != null); print("Push Messaging token: $token"); }); _firebaseMessaging.subscribeToTopic("/topics/all"); } // ダイアログを表示するメソッド void _buildDialog(BuildContext context, String message) { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return new AlertDialog( content: new Text("Message: $message"), actions: <Widget>[ new FlatButton( child: const Text('CLOSE'), onPressed: () { Navigator.pop(context, false); }, ), new FlatButton( child: const Text('SHOW'), onPressed: () { Navigator.pop(context, true); }, ), ], ); } ); }
getTokenメソッドでFCMのトークンを取得しています。requestNotificationPermissionsでpush通知の許可を聞くようにしています。onMessage, onLaunch, onResumeメソッドはpush通知のアクションです。iOS, Android、そして通知のタイプ(notification, data messseage)、そしてアプリの状態によってどのメソッドが呼ばれるか変わります。詳細についてはこちらのページのReceiving Message
を参照してください。アプリを入れている端末全体にpush通知を飛ばすために、initStateでトピック/topics/all
をsubscribeしています。_buildDialog
メソッドでダイアログを表示します。
実際にpush通知を出してみる。
curlでpush通知を送ってみます。your_server_key
の部分には、Firebaseのコンソール画面に移動して、設定->クラウドメッセージングのページに記載されている、サーバーキーを設定してください。
curl -H "Authorization: key=<your_server_key>"\ -H "Content-Type: application/json"\ -d @json/data.json\ https://fcm.googleapis.com/fcm/send
json/data.jsonの中身
{ "to": "/topics/all", "priority": "high", "notification" : { "title" : "テスト Title", "body" : "テスト Body" }, "data" : { "id" : "1001", "message" : "message 文字列", "metadata" : "metadata 文字列", "click_action": "FLUTTER_NOTIFICATION_CLICK" } }
Androidでの注意点
通知をタップして、onResume、onLaunchを発生させるには"click_action" : "FLUTTER_NOTIFICATION_CLICK"をdata
の中に含める必要があります。
結果
iOS
Android
解決しなかったこと
Androidの通知の仕組み詳しくないのですが、アプリがTerminatedの時には届かなかったです。プラグインのREADME.mdには確かにterminatedの時にData messageは届かない、と記載がありますが、notificationでかつdataを含む場合でも、届かなかったです。Androidのバージョンによっても挙動が違うのかな。。。(今回使用した端末は6.0と7.0)アプリがバックグラウンド、フォアグラウンドに存在するときは正常に届きました。
最後に
余暇で楽しんでいる程度ですが、最近社内でも「Flutter触っているらしいですね」と、声をかけてくださる方が増えて嬉しいです。これからもがんばろー。